/*
 * @Descripttion: 功能封装类（除了基础渲染功能之外的功能在此定义）
 * @version: V1.0.0.1
 * @Author: zhaozhenzhuo
 * @Date: 2025-01-20 13:40:11
 * @LastEditors: zhaozhenzhuo
 * @LastEditTime: 2025-02-18 16:06:39
 */
import { Events } from 'xgplayer';
import { MultiPlayerOptionsRequireOne, MultiPlayerClassOptions, PlayerListOptions } from './types';
import { createDom } from './utils';
import { DEFAULT_CONFIG, CLASS_CONFIG } from './config';

type Constructor<T = object> = new (...args: any[]) => T;

// 确保基类实现了 BaseClass 接口
export function FunctionMixin<TBase extends Constructor<any>>(Base: TBase): TBase {
	return class extends Base {
		private errorTips: MultiPlayerClassOptions['errorTips']; // 自定义错误提示
		private switchCallback: MultiPlayerClassOptions['switchCallback']; // 切换画面后的回调
		private syncInterval: MultiPlayerClassOptions['syncInterval']; // 定时器
		private isDragging: MultiPlayerClassOptions['isDragging']; // 当前是否正在拖动音量条
		private originHasVol: MultiPlayerClassOptions['originHasVol']; // 保存原始的hasVol，因为记忆音量是会重新设置hasVol，取消记忆时需要恢复
		private resizeObserverArr: MultiPlayerClassOptions['resizeObserverArr']; // 保存监听resize事件对象，便于播放器销毁时取消监听

		constructor(...args: any[]) {
			super(...args);
			const options = args[0] as MultiPlayerOptionsRequireOne;
			this.errorTips = options.errorTips ?? DEFAULT_CONFIG.errorTips;
			this.switchCallback = options.switchCallback;
			this.syncInterval = null;
			this.isDragging = false;
			this.originHasVol = this.hasVol;
			this.resizeObserverArr = [];
			console.log('playerList', this.playerList);
			// 初始化功能配置
			this.initFunction();
		}

		// 初始化功能配置
		private initFunction(): void {
			// 播放器事件绑定
			this.playerEventBind();
			// 播放器点击事件
			this.playerClick();
			// 音量记忆
			if (this.enableVolumeMemory) {
				this.setVolumeMemory();
			}
			// 设置错误提示
			this.setErrorTips();
			// 如果只剩一个画面，去除这个画面的播放完毕状态
			if (this.playerList && this.playerList.length === 1) {
				this.playerList[0].root.classList.remove(CLASS_CONFIG.VIDEO_IS_FINISH);
			}
		}

		// 播放器事件绑定
		private playerEventBind(): void {
			const _playerList: PlayerListOptions[] = this.playerList;
			for (let i = 0; i < _playerList.length; i++) {
				if (_playerList[i].player) {
					// 一播全播
					// 主画面绑定播放事件
					if (_playerList[i]._playEvent) {
						_playerList[i].player?.off(Events.PLAY, _playerList[i]._playEvent as () => void);
					}
					_playerList[i]._playEvent = (): void => {
						if (_playerList[i].isLarge) {
							if (!this.isLive) {
								// 开启轮询同步
								this.syncPlayers(true);
								// 开始轮询同步
								if (!this.syncInterval) {
									this.syncInterval = setInterval(
										() => this.syncPlayers(false),
										1000
									) as ReturnType<typeof setInterval>;
								}
							} else {
								// 直播状态，不开启轮询，直接全部播放
								for (let j = 0; j < _playerList.length; j++) {
									if (!_playerList[j].isLarge && _playerList[j].player) {
										_playerList[j].player?.play();
									}
								}
							}
						}
					};

					_playerList[i].player?.on(Events.PLAY, _playerList[i]._playEvent as () => void);

					// 暂停全停
					if (_playerList[i]._pauseEvent) {
						_playerList[i].player?.off(
							Events.PAUSE,
							_playerList[i]._pauseEvent as (e: HTMLVideoElement) => void
						);
					}
					_playerList[i]._pauseEvent = (e: HTMLVideoElement): void => {
						if (_playerList[i].isLarge) {
							// 主画面暂停时，如果主画面已经播完，说明是自动暂停，需要根据主画面和小画面的时长判断小画面是否需要播放
							// 如果主画面没有播完，说明是手动暂停，需要将所有画面全部暂停
							if (e.ended) {
								// 自动暂停
								this.syncPlayers(true);
							} else {
								// 手动暂停
								for (let j = 0; j < _playerList.length; j++) {
									if (!_playerList[j].isLarge && _playerList[j].player) {
										_playerList[j].player?.pause();
									}
								}
							}
							if (this.syncInterval) {
								// 停止同步
								clearInterval(this.syncInterval);
								this.syncInterval = null;
							}
						}
					};

					_playerList[i].player?.on(
						Events.PAUSE,
						_playerList[i]._pauseEvent as (e: HTMLVideoElement) => void
					);

					// 播放器跳转
					if (_playerList[i]._seekedEvent) {
						_playerList[i].player?.off(Events.SEEKED, _playerList[i]._seekedEvent as () => void);
					}
					_playerList[i]._seekedEvent = (): void => {
						if (_playerList[i].isLarge) {
							// 主画面播放，小画面从播放完毕状态切换到正常播放状态后会自动播放，为了统一，主画面seek后也强制播放
							_playerList[i].player?.play();
							// 立即进行一次同步
							this.syncPlayers(true);
						}
					};
					_playerList[i].player?.on(Events.SEEKED, _playerList[i]._seekedEvent as () => void);
				}
			}
		}

		// 多画面同步
		private syncPlayers(immediate = false): void {
			const _playerList = this.playerList;
			let _masterPlayer: PlayerListOptions | null = null;
			// 设置主画面
			_playerList.forEach((item: PlayerListOptions) => {
				if (item.isLarge) {
					_masterPlayer = item;
				}
			});

			if (!_masterPlayer || this.isLive) {
				return;
			}
			const _masterTime = (_masterPlayer as PlayerListOptions).player?.currentTime;

			_playerList.forEach((item: PlayerListOptions) => {
				if (!item.isLarge && item.player) {
					let _deviationDiffTime = 0;
					if (
						_masterPlayer &&
						(_masterPlayer.deviationTime || _masterPlayer.deviationTime === 0) &&
						(item.deviationTime || item.deviationTime === 0)
					) {
						_deviationDiffTime = item.deviationTime - _masterPlayer.deviationTime;
					}
					const diffBeginTime = Math.abs(_deviationDiffTime);
					const diffTime = _deviationDiffTime + _masterTime;

					// 主画面和小画面误差超过0.5秒则同步
					if (
						immediate ||
						Math.abs(diffBeginTime - Math.abs(item.player.currentTime - _masterTime)) >= 0.5
					) {
						if (item.player.duration && !this.isLive && item.player.duration > diffTime) {
							// 主画面的开始时间-小画面的开始时间+主画面的当前播放时间
							const _currentTime = diffTime < 0 ? 0 : diffTime;
							item.player.currentTime = _currentTime;
							if (item.player.paused) {
								item.player.play();
							}
							console.log('同步画面', _currentTime);
							// 解除视频播放完毕状态
							item.root?.classList.remove(CLASS_CONFIG.VIDEO_IS_FINISH);
						} else if (item.player.duration && !this.isLive) {
							// 小画面应该显示“播放结束”
							item.player.currentTime = item.player.duration;
							if (!item.player.paused) {
								item.player.pause();
							}
							console.log('播放时超过时间暂停');
							// 视频播放完毕状态
							item.root?.classList.add(CLASS_CONFIG.VIDEO_IS_FINISH);
						}
					}
				}
			});
		}

		// 播放器点击事件
		private playerClick(): void {
			const _playerList: PlayerListOptions[] = this.playerList;
			for (let i = 0; i < _playerList.length; i++) {
				if (_playerList[i].root) {
					// 播放器双击
					if (_playerList[i]._dbclickEvent) {
						_playerList[i].root?.removeEventListener(
							'dblclick',
							_playerList[i]._dbclickEvent as () => void
						);
					}
					_playerList[i]._dbclickEvent = (): void => {
						// 触发双击事件时清除单击事件的定时器，防止触发单击事件
						if (_playerList[i].timer) {
							window.clearTimeout(_playerList[i].timer as number);
							_playerList[i].timer = null;
						}
					};
					_playerList[i].root?.addEventListener(
						'dblclick',
						_playerList[i]._dbclickEvent as () => void
					);

					// 播放器单击
					if (_playerList[i]._clickEvent) {
						_playerList[i].root?.removeEventListener(
							'click',
							_playerList[i]._clickEvent as (e: Event) => void
						);
					}
					_playerList[i]._clickEvent = (e: Event): void => {
						e.stopPropagation();
						// 点击控制栏不触发下面的逻辑
						const controlsRef = _playerList[i].root?.querySelector('.xgplayer-controls');
						if (controlsRef && controlsRef.contains(e.target as Node)) {
							return;
						}
						if (_playerList[i].timer) {
							window.clearTimeout(_playerList[i].timer as number);
							_playerList[i].timer = null;
						}
						// 点击小画面，大屏与小屏交换位置
						_playerList[i].timer = window.setTimeout(() => {
							if (_playerList[i].isLarge) {
								// 点击主画面，设置主画面播放/暂停状态，由于禁用了xgplayer默认的点击事件，需要在这里手动添加
								if (_playerList[i].player && _playerList[i].player?.paused) {
									_playerList[i].player?.play();
								} else if (_playerList[i].player) {
									_playerList[i].player?.pause();
								}
							} else {
								// 获取主画面
								const _large = _playerList.find((item) => item.isLarge);
								const _largeClassItem = (
									this.classList as MultiPlayerClassOptions['classList']
								).find((item) => item.isLarge);
								const _largeClass = _largeClassItem?.className;
								// 获取点击的画面
								const _current = _playerList[i];
								const _currentClassItem = (
									this.classList as MultiPlayerClassOptions['classList']
								).find((item) => item.usedId === _current.id);
								const _currentClass = _currentClassItem?.className;
								// 交换样式，重新设置isLarge和usedId属性
								if (
									_large &&
									_largeClassItem &&
									_largeClass &&
									_current &&
									_currentClassItem &&
									_currentClass
								) {
									_current.isLarge = true;
									_currentClassItem.usedId = _largeClassItem.usedId;
									(_current.root?.parentNode as HTMLElement).classList.remove(_currentClass);
									(_current.root?.parentNode as HTMLElement).classList.add(_largeClass);
									_large.isLarge = false;
									_largeClassItem.usedId = _playerList[i].id;
									(_large.root?.parentNode as HTMLElement).classList.remove(_largeClass);
									(_large.root?.parentNode as HTMLElement).classList.add(_currentClass);
									// 重新设置音量
									if (_current.player && !this.hasVol) {
										// 如果采用默认主画面有音量的模式，在每次切换大小画面时，重新设置主画面有音量
										_current.player.volume = DEFAULT_CONFIG.volume;
									}
									// 重新设置是否同步
									if (_current.player && _current.player.paused && this.syncInterval) {
										// 切换后主画面是暂停状态，就停止同步
										clearInterval(this.syncInterval);
										this.syncInterval = null;
									} else if (_current.player && !_current.player.paused && !this.syncInterval) {
										// 切换后主画面是播放状态，就重新同步
										this.syncInterval = setInterval(() => this.syncPlayers(false), 1000);
									}
									console.log('画面切换成功');
									console.log('playerList', this.playerList);
									// 调用切换画面回调
									if (this.switchCallback) {
										this.switchCallback(this.playerList);
									}
								}
							}
						}, 300);
					};
					_playerList[i].root?.addEventListener(
						'click',
						_playerList[i]._clickEvent as (e: Event) => void
					);
				}
			}
		}

		// 设置音量记忆
		private setVolumeMemory(): void {
			this.resizeObserverArr = [];
			const _playerList: PlayerListOptions[] = this.playerList;
			for (let i = 0; i < _playerList.length; i++) {
				// 监听可以流畅播放
				if (_playerList[i]._canplayEvent1) {
					_playerList[i].player?.off(Events.CANPLAY, _playerList[i]._canplayEvent1 as () => void);
				}
				_playerList[i]._canplayEvent1 = (): void => {
					// 给音量键绑定点击事件，判断是否是用户手动修改音量
					const _volumeArr: NodeListOf<HTMLElement & { events: any }> = document.querySelectorAll(
						'.xgplayer-volume .xgplayer-icon'
					);
					if (_volumeArr && _volumeArr.length) {
						_volumeArr.forEach((item) => {
							// 检查是否已绑定过事件处理器
							if (!item.events) item.events = {};
							if (!item.events['click']) {
								item.events['click'] = [];
							}
							// 如果事件处理器之前没有绑定过，则绑定它
							if (item.events['click'].indexOf('volumeClickEvent') === -1) {
								item.addEventListener('click', () => {
									// 找到对应的id
									const _multiLayer = document.querySelectorAll(`.${CLASS_CONFIG.MULTI_LAYER}`);
									_multiLayer.forEach((i) => {
										if (i.contains(item)) {
											const _video = i.querySelector('video');
											console.log('是否静音', _video?.muted);
											if (_video?.muted) {
												// 重新设置默认
												this.setHasVol(i.id.split('_')[1]);
											} else {
												this.setHasVol('');
											}
										}
									});
								});
								item.events['click'].push('volumeClickEvent');
							}
						});
					}

					// 监听xgplayer-drag音量条高度变化，判断是否是用户手动修改音量
					const _dragArr: NodeListOf<HTMLElement & { events: any }> = document.querySelectorAll(
						'.xgplayer-volume .xgplayer-drag'
					);
					if (_dragArr && _dragArr.length) {
						_dragArr.forEach((item) => {
							// 检查是否已绑定过事件处理器
							if (!item.events) item.events = {};
							if (!item.events['observer']) {
								item.events['observer'] = [];
							}
							// 如果事件处理器之前没有绑定过，则绑定它
							if (item.events['observer'].indexOf('resizeObserver') === -1) {
								const resizeObserver = new ResizeObserver(() => {
									// 当DOM元素的宽高发生变化时执行回调函数
									// 只有正在拖拽时才算
									if (this.isDragging) {
										// 找到对应的videoId
										const _multiLayer = document.querySelectorAll(`.${CLASS_CONFIG.MULTI_LAYER}`);
										_multiLayer.forEach((i) => {
											if (i.contains(item)) {
												console.log('音量', item.clientHeight);
												if (item.clientHeight) {
													this.setHasVol(i.id.split('_')[1]);
												} else {
													this.setHasVol('');
												}
											}
										});
									}
								});
								resizeObserver.observe(item);
								this.resizeObserverArr.push(resizeObserver);
								item.events['observer'].push('resizeObserver');
							}
						});
					}

					// 监听鼠标是否在xgplayer-slider内，间接判断是否是用户拖动的音量条
					const _barArr: NodeListOf<HTMLElement & { events: any }> = document.querySelectorAll(
						'.xgplayer-volume .xgplayer-bar'
					);
					if (_barArr && _barArr.length) {
						_barArr.forEach((item) => {
							// 检查是否已绑定过事件处理器
							if (!item.events) item.events = {};
							if (!item.events['mousedown']) {
								item.events['mousedown'] = [];
							}
							// 如果事件处理器之前没有绑定过，则绑定它
							if (item.events['mousedown'].indexOf('mousedownEvent') === -1) {
								item.addEventListener('mousedown', () => {
									this.isDragging = true;
								});
								item.events['mousedown'].push('mousedownEvent');
							}
						});
					}
					document.addEventListener('mouseup', () => {
						setTimeout(() => {
							this.isDragging = false;
						}, 100);
					});
				};
				_playerList[i].player?.on(Events.CANPLAY, _playerList[i]._canplayEvent1 as () => void);
			}
		}

		// 设置当前记忆的播放音量的画面
		private setHasVol(id: string): void {
			if (id) {
				// 记忆该画面
				this.hasVol = true;
			} else {
				// 取消记忆
				this.hasVol = this.originHasVol;
			}
		}

		// 设置错误提示
		private setErrorTips(): void {
			const _styleStr = `\
          .xgplayer-error-tips {\
            display: none;\
          }\
          .xgplayer-error {\
            z-index: 126;\
            cursor: default;\
          }\
          .xgplayer-error::after {\
            content: '${this.errorTips || '不支持的音频/视频格式'}';\
            position: absolute;\
            top: 0;\
            left: 0;\
            z-index: 126;\
            display: flex;\
            justify-content: center;\
            align-items: center;\
            width: 100%;\
            height: 100%;\
            background: #000;\
            font-size: 14px;\
            font-family: 'Alibaba PuHuiTi';\
            color: #fff;\
          }\
      `;
			let styleEl = document.getElementById(CLASS_CONFIG.MULTI_PLAYER_ERROR_STYLE);
			if (!styleEl) {
				// 其次设置容器内部每个播放器错误的样式
				styleEl = createDom('style', {
					id: CLASS_CONFIG.MULTI_PLAYER_ERROR_STYLE,
					type: 'text/css'
				});
				if (styleEl) {
					styleEl.innerHTML = _styleStr;
					document.head.appendChild(styleEl);
				}
			} else {
				styleEl.innerHTML = _styleStr;
			}
		}
	};
}
